﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using pub.GUI;
using pub.Interop;
using pub.Types;
using todo.Interop;
using todo.Model;
using todo.Persistence;
using todo.Plugin;

namespace todo
{
    public partial class FrmMain : Form, IMessageFilter
    {
        private LastWindowState _lastWindowState;
        private readonly IntPtr _checkHideMenuTimerId = (IntPtr) 2009;

        private string fileNameToOpen;

        private LoadMask _loadMask;

        private Interop.Interop _interop;

        private const int COPY_DATA_TYPE_ACTIVE_TODO = 2;
        private bool _firstReady;

        public FrmMain()
        {
            InitializeComponent();
            //
            _lastWindowState = new LastWindowState(App.Settings["WindowState"], this);
            _lastWindowState.OnStateLoaded += (sender, args) => {
                _loadMask = new LoadMask(this, www);
//                
            };
            //
            _interop = new Interop.Interop();
            TodoApi.Instance = new TodoApi(www);

            InitMru();

            //
            App.DirtyChanged += App_DirtyChanged;
            this.FormClosing += OnFormClosing;

            //
            var host = new Host();
            host.OnDocReady += (sender, eventArgs) => {
                _loadMask.Hide();
                www.Visible = true;
                www.Focus();
                www.Document.Body.Focus();
                www.Document.InvokeScript("setInterop", new object[] {_interop});
                TryFocusWww();

                if (!_firstReady)
                {
                    OnFirstReady();
                    _firstReady = true;
                }

                ++TodoApi.Instance.DocChangeCounter;
                App.CmdUIManager.InvalidateCmdUI();
            };

            host.OnSaveRequest += (sender, args) => { Save(); };

            host.OnSaveAndQuitRequest += (sender, args) => {
                if (Save())
                {
                    this.Close();
                }
            };

            www.ObjectForScripting = host;

            //
            if (!App.Settings.ShowMenu)
            {
                Debug.Print("hide menu @ constructor");
//                Win32.SetMenu(this.Handle, IntPtr.Zero);
                this.Menu = null;
            }

            Application.AddMessageFilter(this);

        }

        public void InitUI()
        {
            if (App.Settings.ShowRibbonUI)
            {
                InitRibbonUI();
                this.Menu = null;
                this._ribbonUi.Load();
                LoadRibbonSettings();
            }
            else
            {
                InitClassicUI();
            }
        }

        private void OnFirstReady()
        {
            var args = Environment.GetCommandLineArgs();
            for (var n = 1; n < args.Length; n++)
            {
                var arg = args[n];
                if (arg == "/activeTodo")
                {
                    var bts = Convert.FromBase64String(args[n + 1]);
                    var pathTitles = Encoding.Default.GetString(bts);
                    TodoApi.Instance.SetActiveTodo(pathTitles);
                    ++n;
                }
            }
        }

        private void UpdateTitle(string filename = null)
        {
            if (filename == null)
            {
                filename = App.TodoListFile ?? App.TempNewFile;
            }
            var text = "Todo.EXE - [" + filename;
            if (App.Dirty)
            {
                text += " *";
            }
            text += "]";
            this.Text = text;
        }

        private void OnFormClosing(object sender, FormClosingEventArgs e)
        {
            if (CheckDirty())
            {
                var result = PromptSaveDirty();
                if (result == DialogResult.Cancel)
                {
                    e.Cancel = true;
                    return;
                }
                if (result == DialogResult.Yes)
                {
                    if (!Save())
                    {
                        e.Cancel = true;
                        return;
                    }
                }
            }

            App.Settings.ShowRibbonUI = ShowingRibbon;

            //
            if (App.TodoListFile != null && App.TodoListFile != App.TempNewFile)
            {
                OnFileClose(App.TodoListFile);
            }

            //
            _loadMask.Dispose();
        }

        private static DialogResult PromptSaveDirty()
        {
            var prompt = "Do you want to save changes to " + App.TodoListFile + "?";
#if zh
            prompt = "文件 " + App.TodoListFile + " 的内容已经改变。\n" +
                "想保存文件吗？";
#endif
            var result = MessageBox.Show(prompt, App.Title, MessageBoxButtons.YesNoCancel,
                    MessageBoxIcon.Exclamation);
            return result;
        }

        private void App_DirtyChanged(object sender, EventArgs e)
        {
            UpdateTitle();
        }

        public List<Todo> GetTodos()
        {
            www.Document.InvokeScript("commitEdit");

            var todosResult = new TodosResult();
            www.Document.InvokeScript("getTodos", new object[] {todosResult});
            return todosResult.Todos.GetList();
        }

        public bool Save(bool saveAs = false)
        {
            var file = App.TodoListFile;
            if (App.TempNewFile.Equals(file) || saveAs)
            {
                dlgSave.DefaultExt = "todolist";
                dlgSave.Filter = "TodoList files (*.todolist)|*.todolist|All files (*.*)|*.*";
                dlgSave.FileName = Path.GetFileName(file);
                var result = dlgSave.ShowDialog();
                if (result != DialogResult.OK)
                {
                    return false;
                }
                file = App.TodoListFile = dlgSave.FileName;
            }
            else
            {
                Backup();
            }

            App.TodoList.Todos = GetTodos();
            App.TodoList.UpdateTime = DateTime.Now;
            AppFuncs.UpdateTransientInfos(App.TodoList);

            TodoListWriter.Save(App.TodoList, file);
            App.Dirty = false;

            UseFile(file);

            if (saveAs)
            {
                App.TodoListFile = file;
            }
            UpdateTitle();

            //
            PluginManager.Instance.OnSave(file, App.TodoList);

            return true;
        }

        private void Backup()
        {
            var fileName = App.TodoListFile;
            if (!File.Exists(fileName))
            {
                Debug.Print("something wrong.. " + fileName + " not found when backing up");
                return;
            }
            var backupDir = Path.Combine(App.DataPath, "backup");
            Directory.CreateDirectory(backupDir);
            var backupFileName = fileName.Replace(':', '#').Replace('\\', '#').Replace('/', '#');
            backupFileName = Path.Combine(backupDir, backupFileName);
            File.Copy(fileName, backupFileName, true);
        }

        private void FrmMain_Shown(object sender, EventArgs e)
        {
            Debug.Print("FrmMain_Shown");


            //
            App.NotifyStarted();
        }

        private bool CheckDirty()
        {
            if (App.TodoListFile == null || www.Document == null)
            {
                return false;
            }
            www.Document.InvokeScript("commitEdit");
            return App.Dirty;
        }

        public void New()
        {
            if (CheckDirty())
            {
                if (!SaveDirty())
                {
                    return;
                }
            }
            if (App.TodoListFile != null && App.TodoListFile != App.TempNewFile)
            {
                OnFileClose(App.TodoListFile);
            }
            App.NewTodoList();
            App.Dirty = false;
            www.Refresh();
            UpdateTitle();
            //
        }

        // return canceled
        private bool SaveDirty()
        {
            var result = PromptSaveDirty();
            if (result == DialogResult.Cancel)
            {
                return false;
            }
            if (result == DialogResult.Yes)
            {
                if (!Save())
                {
                    return false;
                }
            }
            return true;
        }

        public bool OnOpen(string fileName = null)
        {
            if (CheckDirty())
            {
                if (!SaveDirty())
                {
                    return false;
                }
            }
            if (fileName == null)
            {
                dlgOpen.DefaultExt = "todolist";
                dlgOpen.Filter = "TodoList files (*.todolist)|*.todolist|All files (*.*)|*.*";
                var result = dlgOpen.ShowDialog();
                if (result != DialogResult.OK)
                {
                    return false;
                }
                fileName = dlgOpen.FileName;
            }

            //
            try
            {
                PluginManager.Instance.OnOpen(fileName);
            }
            catch (AbortException e)
            {
                return false;
            }
            catch(Exception e)
            {
                App.OnUnknownException(e);
                return false;
            }

            Open(fileName);
            return true;
        }

        public void Open(string fileName)
        {
            if (App.TodoListFile != null && App.TodoListFile != App.TempNewFile)
            {
                OnFileClose(App.TodoListFile);
            }

            //
            this.fileNameToOpen = fileName;
            Win32.SHAddToRecentDocs(Win32.ShellAddToRecentDocsFlags.Path, fileName);
            timerLoad.Enabled = true;
        }

        protected override void WndProc(ref Message m)
        {
            if (m.Msg == Win32.WM_NCCALCSIZE)
            {
                if (_ribbonUi != null && _ribbonUi.Loaded && _ribbonUi.Hidden)
                {
                    _ribbonUi.UpdateHiddenPlaceholderHeight();
                }
            }
            else if (m.Msg == Win32.WM_WINDOWPOSCHANGED)
            {
                typeof (Form).GetField("restoredWindowBoundsSpecified",
                        BindingFlags.NonPublic | BindingFlags.Instance).SetValue(this,
                                BoundsSpecified.None);
            }
            else if (m.Msg == Win32.WM_COPYDATA)
            {
                var cds = (Win32.COPYDATASTRUCT) Marshal.PtrToStructure(
                        m.LParam, typeof (Win32.COPYDATASTRUCT));
                ProcessCopyData((int) cds.dwData, cds.cbData, cds.lpData);
            }
            else if (m.Msg == Win32.WM_TIMER)
            {
                if (m.WParam == _checkHideMenuTimerId)
                {
                    Win32.KillTimer(this.Handle, _checkHideMenuTimerId);
                    if (!App.Settings.ShowMenu)
                    {
                        Debug.Print("hide menu @ WM_TIMER");
                        this.Menu = null;
                        if (_ribbonUi != null)
                        {
                            _ribbonUi.NotifyOwnerFormMenuChanged();
                        }
                    }
                }
            }
            else if (m.Msg == Win32.WM_EXITMENULOOP)
            {
                Debug.Print("hide menu @ WM_EXITMENULOOP");
                Win32.SetTimer(this.Handle, _checkHideMenuTimerId, 100, IntPtr.Zero);
                //                this.Menu = null;
            }
            base.WndProc(ref m);
        }

        private void ProcessCopyData(int type, int cbData, string lpData)
        {
            if (type == COPY_DATA_TYPE_ACTIVE_TODO)
            {
                if (TodoApi.Instance.Ready)
                {
                    TodoApi.Instance.SetActiveTodo(lpData);
                }
                else
                {
                    Debug.Print("webui not ready for COPY_DATA_TYPE_ACTIVE_TODO");
                }
            }
        }

        public bool PreFilterMessage(ref Message m)
        {
            if (m.Msg == Win32.WM_SYSKEYDOWN)
            {
                if (m.WParam == (IntPtr) Win32.VK_MENU && this.Menu == null && !ShowingRibbon)
                {
                    this.Menu = mainMenu1;
                }
            }
            return false;
        }

        private void www_Navigating(object sender, WebBrowserNavigatingEventArgs e)
        {
            var url = e.Url.ToString();
            if (url.StartsWith("http"))
            {
                Process.Start(url);
                e.Cancel = true;
            }
        }

        private void FrmMain_Load(object sender, EventArgs e)
        {
            Debug.Print("FrmMain_Load");
            var args = Environment.GetCommandLineArgs();
            UpdateTitle(args.Length > 1 ? args[1] : null);
        }

        private void timerLoad_Tick(object sender, EventArgs e)
        {
            timerLoad.Enabled = false;
            www.Visible = false;
            _loadMask.Show();
//            Thread.Sleep(3000);

            var fileName = this.fileNameToOpen;
            //
            if (fileName == null)
            {
                App.NewTodoList();
            }
            else
            {
                App.TodoList = TodoListReader.Read(fileName);
                App.TodoListFile = fileName;
            }
            App.Dirty = false;


            InitWebUI();


            UpdateTitle();
            //
            UseFile(fileName);
        }

        private void InitWebUI()
        {
            var exe = Assembly.GetExecutingAssembly().Location;
            var url = @"res://" + exe + "/todo.html";
            www.Navigate(url);
        }

        private void FrmMain_Activated(object sender, EventArgs e)
        {
            TryFocusWww();
            www.CheckRestoreDefaultPageSetup();
        }

        private void TryFocusWww()
        {
            if (!this.Visible || this.WindowState == FormWindowState.Minimized || !www.Visible ||
                www.Document == null)
            {
                return;
            }
            www.Focus();
            www.Document.InvokeScript("gotFocus");
        }

        private void FrmMain_SizeChanged(object sender, EventArgs e)
        {
            timerFocus.Enabled = true;
        }

        private void timerFocus_Tick(object sender, EventArgs e)
        {
            timerFocus.Enabled = false;
            TryFocusWww();
        }

        public WebBrowserEx Www
        {
            get { return www; }
        }

        private void OnFileClose(string file)
        {
            var listStateSettings = App.Settings["ListState"][file];
            if (this.WindowState != FormWindowState.Minimized)
            {
                var sWidths = www.Document.InvokeScript("getColWidths") as string;
                if (sWidths != null)
                {
                    var colWidths = Interop.Interop.ParseDoubleList(sWidths, ',');
                    var sColWidths = AppFuncs.GetColWidthsString(colWidths);
                    listStateSettings["ColWidths"].StringValue = sColWidths;
                }
            }
            // don't save collapsed state when dirty
            if (App.Dirty) 
            {
                return;
            }
            //
            var collapsedItemInfosResult = new CollapsedItemInfosResult();
            www.Document.InvokeScript("getCollapsedItems", new object[] {collapsedItemInfosResult});
            var collapsedItems = collapsedItemInfosResult.Value.GetList();

            var flatTodoList = new List<Todo>();
            flatTodoList = AppFuncs.GetFlatTodoList(App.TodoList);

            var ms = new MemoryStream();
            var writer = new BinaryWriter(ms);
            foreach (var info in collapsedItems)
            {
                var todo = flatTodoList[info.Row - 1];
                var pathTitle = AppFuncs.GetPathTitle(todo);
                writer.Write(pathTitle.GetHashCode());
                //writer.Write(info.Title.GetHashCode());// + info.Title.Length);
            }
            var bts = ms.ToArray();
            writer.Close();
            ms.Close();

            listStateSettings["CollapsedItems"].BytesValue = bts;
        }
        
        protected override CreateParams CreateParams
        {
            get
            {
                var createParams = base.CreateParams;
                createParams.Style |= 0x02000000;
                return createParams;
            }
        }

        private void timerStartup_Tick(object sender, EventArgs e)
        {
            timerStartup.Enabled = false;
            _loadMask.Show();
            var args = Environment.GetCommandLineArgs();
            string fileToOpen = null;
            if (args.Length > 1)
            {
                fileToOpen = args[1];
            }
            else if (App.IsFirstInstance && App.Settings.OpenLastFileOnStart && _mru.ItemCount > 0)
            {
                
                fileToOpen = _mru.ItemAt(0).Value;
            }

            if(fileToOpen != null)
            {
                if (!OnOpen(fileToOpen))
                {
                    App.NewTodoList();
                    InitWebUI();
                }
            }
            else
            {
                App.NewTodoList();
                InitWebUI();                
            }

            timerFocus.Enabled = true;
        }

        private void mnViewRibbonUI_Click(object sender, EventArgs e)
        {
            if (this._ribbonUi == null)
            {
                this.InitRibbonUI();
            }
            if (this._ribbonUi.Loaded)
            {
                this._ribbonUi.Show();
                this.Menu = null;
            }
            else
            {
                this.Menu = null;
                this._ribbonUi.Load();
                LoadRibbonSettings();
            }
        }

        private void FrmMain_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (_ribbonUi != null && _ribbonUi.Loaded)
            {
                SaveRibbonSettings();
                _ribbonUi.Unload();
            }
            www.CheckRestoreDefaultPageSetup();
        }

        private void FrmMain_DragDrop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                var files = (string[]) e.Data.GetData(DataFormats.FileDrop);
                if (files.Length > 0)
                {
                    OnOpen(files[0]);
                }
            }
        }

        private void FrmMain_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                e.Effect = DragDropEffects.Copy;
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }
        }

        public void ReloadTodoList()
        {
            UpdateTitle();
            InitWebUI();
        }
    }
}